package cn.lyd.base.service.impl;

import cn.lyd.base.cons.PhoneRegisterCon;
import cn.lyd.base.cons.WechatLoginCon;
import cn.lyd.base.dto.BinderDto;
import cn.lyd.base.dto.WechatDto;
import cn.lyd.base.jwt.JwtUtils;
import cn.lyd.base.jwt.RsaUtils;
import cn.lyd.base.jwt.UserInfo;
import cn.lyd.base.service.ILoginService;
import cn.lyd.base.until.*;
import cn.lyd.base.vo.LoginVo;
import cn.lyd.system.domain.Menu;
import cn.lyd.system.mapper.MenuMapper;
import cn.lyd.system.mapper.PermissionMapper;
import cn.lyd.user.domain.Logininfo;
import cn.lyd.user.domain.User;
import cn.lyd.user.domain.Wxuser;
import cn.lyd.user.mapper.LogininfoMapper;
import cn.lyd.user.mapper.UserMapper;
import cn.lyd.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import springfox.documentation.spring.web.json.Json;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private MenuMapper menuMapper;

    //获取yml配置中的jwt私钥-用来加密
    @Value("${jwt.lyd.pri}")
    private  String pri;


    /**
     * 账号密码登录
     * @param loginVo
     * @return
     */
    @Override
    public Map<String, Object> account(LoginVo loginVo) {

        //1.参数空值校验-注解实现

        //2.查询数据库判断是否存在该账号
         Logininfo logininfo = logininfoMapper.findByAccountAndType(loginVo);
        if (logininfo==null){
            throw new BussinessException("账号或者密码错误，请重新输入");
        }

        //3.判断密码是否一致
        //前端填的密码 加盐加密                         //日志里的盐值              //前端填的密码
        String encrypByMd5 = MD5Utils.encrypByMd5(logininfo.getSalt() + loginVo.getPassword());
        //如果前端填的密码加盐加密后和日志里保存的密码不一致==密码错误
        if(!logininfo.getPassword().equals(encrypByMd5)){
            throw  new BussinessException("账号或密码有误！");
        }

        //4.redis获取 返回给前端localStorage【本地储存：map kv】结构的数据
        //Map<String, Object> result = getResultToWebByMap(logininfo);



        //jwt获取 用户登录成功后生成JWT加密串并放入到map对象中
        Map<String, Object> result = getResultToWebByMapByJwt(logininfo);
        //返回token和用户信息
        return result;
    }

    /**
     * 微信扫码登录
     * @param dto 前端传递的code：用户微信扫码后点击确认授权，在回调页面里截取到地址栏的code参数的值，发起post请求传递到后端
     */
    @Override
    public AjaxResult wechat(WechatDto dto) {

        //1.参数校验  或者用NotBlank注解
        /*if (StringUtils.isEmpty(dto.getCode())){
            throw new BussinessException("传递的code参数不能为空！");
        }*/

        //2.调用第二个请求：通过 code,appid,secret, 获取access_token和openid
        //2.1获取code
        String code = dto.getCode();
        //2.2第二个请求的地址
        String tokenUrl = WechatLoginCon.access_token_url.replace("CODE", code);
        //2.3调用工具类发起请求,返回的是json字符串
        String jsonStr = HttpClientUtils.httpGet(tokenUrl);
        //2.4转换成json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        /*
        正确的返回：
        {
            "access_token":"ACCESS_TOKEN",
                "expires_in":7200,
                "refresh_token":"REFRESH_TOKEN",
                "openid":"OPENID",
                "scope":"SCOPE",
                "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
        }
        错误返回样例：
        {"errcode":40029,"errmsg":"invalid code"}
        */
        //2.5判断是否调用成功，调用失败就抛异常
        String accessToken =null;
        String openId =null;
        try {
            //获取access_token和openid     json格式---"access_token":"ACCESS_TOKEN", "openid":"OPENID",
            accessToken = jsonObject.getString("access_token");
            openId = jsonObject.getString("openid");
            if(accessToken!=null && openId!=null){
                System.out.println("成功获取 接口调用凭证 和 授权用户唯一标识！！！");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //3.通过openid 查询wxuser表的wx用户是否存在
        Wxuser wxuser = wxuserMapper.findByOpenId(openId);
        //3.1 wx用户存在并且已经绑定了账号，直接免密登录
        if (wxuser!=null && wxuser.getUser_id()!=null){
            //3.1.1根据wxuser中的userid查询logininfo信息
            /*Long wxUserId = wxuser.getUserId();
            User userById = userMapper.findById(wxUserId);
            Long userLogininfoId = userById.getLogininfoId();
            Logininfo logininfo = logininfoMapper.findById(userLogininfoId);*/
            /*法二：sql查询*/
            Long wxUserId = wxuser.getUser_id();
            Logininfo logininfo = logininfoMapper.findByUserId(wxUserId);

            //redis获取 返回给前端localStorage【本地储存：map kv】结构的数据
            //Map<String, Object> result = getResultToWebByMap(logininfo);

            //jwt获取 用户登录成功后生成JWT加密串并放入到map对象中
            Map<String, Object> result = getResultToWebByMapByJwt(logininfo);
            return AjaxResult.me().setResultObj(result);
        }
        // 3.2 不存在告诉用户跳转到账号绑定页面，返回access_token和openid给前端 ，因为我们在下个接口需要使用
        StringBuffer sb = new StringBuffer();
        sb.append("?accessToken=").append(accessToken).append("&openId=").append(openId);
        return AjaxResult.me().setSuccess(false).setMsg("nobinder").setResultObj(sb.toString());

    }

    /**
     * 微信绑定
     * @param dto
     * @return
     */
    @Override
    public AjaxResult binder(BinderDto dto) {
        // 校验参数
        // 判断手机验证码是否正确
        String redisSmsCode = (String) redisTemplate.opsForValue().get(String.format(PhoneRegisterCon.BINDER_REDIS_KEY, dto.getPhone()));
        if (StringUtils.isBlank(redisSmsCode) || !redisSmsCode.equals(dto.getVerifyCode())) {
            throw new BussinessException("验证码错误！");
        }

        // 调用第三个接口
        String jsonStr = HttpClientUtils.httpGet(String.format(WechatLoginCon.THIRD_INTER, dto.getAccessToken(), dto.getOpenId()));
        // 将jsonStr转成实体
        Wxuser wxuser = JSONObject.parseObject(jsonStr, Wxuser.class);
        // 判断user里面是否有数据 根据手机号查询user表
        User user = userMapper.findByPhone(dto.getPhone());
        Logininfo logininfo = new Logininfo();
        if (null == user) {
            // 没有数据 保存三张表 loininfo user wxuser
            // 实例化user 实例化logininfo
            user = dto.getUser();
            BeanUtils.copyProperties(user, logininfo);
            logininfo.setType(1);
            logininfo.setDisable(1);
            // 先去添加logininfo对象
            logininfoMapper.add(logininfo);
            user.setLogininfo_id(logininfo.getId());
            userMapper.add(user);
        }
        // 添加wxuser
        wxuser.setUser_id(user.getId());
        wxuserMapper.add(wxuser);
        // 登录成功 返回给前端 token和logininfo
        logininfo = logininfoMapper.findByUserId(wxuser.getUser_id());
        //redis获取 返回给前端localStorage【本地储存：map kv】结构的数据
        //Map<String, Object> result = getResultToWebByMap(logininfo);

        //jwt获取 用户登录成功后生成JWT加密串并放入到map对象中
        Map<String, Object> result = getResultToWebByMapByJwt(logininfo);

        return AjaxResult.me().success(result);
    }


    /**
     * redis    获取 返回给前端localStorage【本地储存：map kv】结构的数据
     * @param logininfo 用户信息
     * @return
     */
    /*private Map<String, Object> getResultToWebByMap(Logininfo logininfo) {
        //1.生成redis的key(通过UUID)=token
        String token = UUID.randomUUID().toString();
        //2.将token 和logininfo【用户信息】数据保存到redis【map kv结构】中,设置有效期30分钟
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        //3.将用户敏感信息清空，因为要返回给前端
        logininfo.setPassword(null);
        logininfo.setSalt(null);
        //4.将token和logininfo【用户信息】 返回给前端=设置token和logininfo到result【返回结果】中
        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        result.put("logininfo", logininfo);
        //5.返回 返回结果的数据 到 前端localStorage【本地储存：map kv结构】
        return result;
    }*/



    /**
     * jwt获取 用户登录成功后生成JWT加密串并放入到map对象中
     * @param logininfo
     * @return
     */
    public HashMap<String, Object> getResultToWebByMapByJwt(Logininfo logininfo){
        // 1.创建UserInfo对象，存储logininfo对象，并用于后续赋值
        UserInfo userInfo = new UserInfo();
        userInfo.setLogininfo(logininfo);
        // 2.判断是后台用户登录才需要设置菜单信息、权限信息，类型为0那么就是后台用户登录
        if(logininfo.getType() == 0){
            // 2.1.获取用户权限信息，我们只需要sn权限编码即可
            List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
            // 2.2.获取用户菜单信息
            List<Menu> menus = menuMapper.findOwnMenuByLogininfoId(logininfo.getId());
            // 2.3.将菜单信息，权限信息放入到userinfo对象中
            userInfo.setPermissions(permissions);
            userInfo.setMenus(menus);
        }
        // 3.根据密钥文件名称获取到密钥
        PrivateKey privateKey = RsaUtils.getPrivateKey(this.getClass().getClassLoader().getResource(pri).getFile());
        // 4.根据密钥、用户信息、获取JWT加密串
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 60);
        // 5.按照正常逻辑我们直接返回token给前端即可，但是那样前端需要解密，我们使用简化版本JWT，还是返回用户信息
        // 封装token、用户信息、权限信息、菜单信息给前端
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("logininfo", logininfo);
        map.put("permissions", userInfo.getPermissions());
        map.put("menus", userInfo.getMenus());
        return map;
    }



}
